﻿// C++ 异常

// throw 抛出异常 , catch 捕获异常
// catch 可以有多个 , try - catch
//抛出异常可以显示具体信息

#include <iostream>
#include <string>
using namespace std;




void Func0(const int a)
{
	string s("这是抛出异常的演示");
	if (a == 2)
		throw s; // 这里抛出异常
	else
		cout << "ok" << endl;
}


//***** 异常与捕获基本概念 ***** 
//try - catch 捕获异常 , throw-抛出异常
void Test01()
{
	int a = 0;
	cin >> a;

	//捕获异常 , try 中写可能发生的异常 , catch 中捕获
	//如果没有异常就不会进行捕获
	//就直接 走 return 0
	try
	{
		Func0(a);
	}
	catch (const string& s)
	{
		cout << s << endl;
	}
}




//***** 机制 1 演示代码 ,通过调试可看 ***** 
// Divide1 函数中没有 try , catch 语句
double Divide1(int a, int b)
{
	
	// 当b == 0时抛出异常
	if (b == 0)
	{
		string s("Divide by zero condition!");
		throw s; // 抛出的是 s 的拷贝 ,s 出了本作用域就销毁了

		//抛出异常后 , throw 后面的语句就不会执行了
		//并且会跳转到 main函数 catch 中
	} 
	else
	{
		return ((double)a / (double)b);
	}
}	

//***** 机制 1 演示代码 ,通过调试可看 ***** 
// Divide2 函数中有 try , catch 语句且类型匹配会走到这里
double Divide2(int a, int b)
{

	try
	{
		// 当b == 0时抛出异常
		if (b == 0)
		{
			string s("Divide by zero condition!");
			throw s;

			//抛出异常后 , throw 后面的语句就不会执行了
		}
		else
		{
			return ((double)a / (double)b);
		}
	}
	
	//因为这里离 throw 最近且类型一样会优先走这里
	catch(const string& s)
	{
		cout << s << endl;
	}

	return 0;
}


//***** 机制 2 演示代码 ,通过调试可看 ***** 
//若在整个调用链中没有找到匹配的 catch 语句来捕获程序就崩溃了
//会调用 terminate 函数来终止程序
double Divide3(int a, int b)
{
		// 当b == 0时抛出异常
		if (b == 0)
		{
			string s("Divide by zero condition!");
			throw s;
		}
		else
		{
			return ((double)a / (double)b);
		}
}


void Func()
{
	int len, time;
	cin >> len >> time;

	try
	{
		//try 中放异常出现的地方  Divide函数
		
		//机制 1 演示
		//cout << Divide1(len, time) << endl; 
		//cout << Divide2(len, time) << endl;

		//机制  2 演示
		cout << Divide3(len, time) << endl;


	} 
	// catch 捕获异常信息
	catch(const char* errmsg)
	{
		cout << errmsg << endl;
	}

	cout << __FUNCTION__ << ":" << __LINE__ << "执行" << endl;
}


//异常与捕获机制
void Test02()
{
	// 机制 1 : throw 抛出异常后 , 会找与其类型最匹配的 catch 语句
	// 可能是当前函数中的 catch , 可能是函数外的 catch 
	while (1)
	{
		try
		{
			//可能出现异常的地方
			Func();
		}
		//当 Divide函数中没有 try - catch , 捕获异常跳转到这里
		//因为抛出的异常信息是 string 类型的
	/*	catch (const string& str)
		{
			cout << str << endl;
		}*/

		//演示机制 2 -- 整个调用链中都没有匹配的
		//catch (const char* str)
		//{
		//	cout << str << endl;
		//}

		//机制 3 
		// catch(...) 可以用于捕获任意异常
		//用于没有匹配的类型时 , 最终会走这里, 程序不会崩溃
		catch (...)
		{
			cout << "未知异常 " << endl;
		}

	}
}



//基类
class Exception
{
public:

	//构造
	Exception(const string& masage , int id)
		:_masage(masage)
		,_id(id)
	{}

	//信息是什么 ? 
	//这里用一个虚函数 , 目的是实现多态场景
	virtual string what() const
	{
		string st = "Exception :";
		st += _masage;
		return st;
	}

	int getid() const 
	{
		return _id;
	}
protected: // 派生类也可用 
	//一般包含以下两点
	string _masage; // 抛出的信息
	int _id; // 编号
};


//派生类 , 继承 Exception 
class HttpException : public Exception
{
	//因为基类被继承下来了 , 所以基类的内容也在 , 所以构造初始化也要初始化基类的
	//但基类的初始化在派生类中需要调用基类的构造函数完成
public:

	HttpException(const string& httpstr , const string& str = "", int id = 0)
		:_httpmasage(httpstr)
		, Exception(str , id)
	{}


	//形成多态
	//重写虚函数
	virtual string what() const
	{
		string st = "HttpException :";
		st += _httpmasage;
		return st;
	}
	

private:
	string _httpmasage; // 增加一个 http 的信息
};


//派生类 , 继承 Exception 
class MysqlException : public Exception
{
	//因为基类被继承下来了 , 所以基类的内容也在 , 所以构造初始化也要初始化基类的
	//但基类的初始化在派生类中需要调用基类的构造函数完成
public:

	MysqlException(const string& mysqlmasage, const string& str, int id)
		:_mysqlmasage(mysqlmasage)
		, Exception(str, id)
	{}


	//形成多态
	//重写虚函数
	virtual string what() const
	{
		string str = "SqlException:";
		str += _masage;
		str += "->";
		str += _mysqlmasage;
		return str;
	}


private:
	string _mysqlmasage; // 增加一个 mysql 的信息
};


void _exception(int a)
{
	//if (rand() % 5 == 0)
	if (a % 5 == 0)
	{
		throw MysqlException("数据库出现问题","张三出现异常" , 100);
	}
	//else if (rand() % 8 == 0)
	else if (a % 8 == 0)
	{
		throw Exception("基类的出现异常" , 10);
	}
	//else if (rand() % 3 == 0)
	else if (a % 3 == 0)
	{
		throw HttpException("网络问题");
	}
	else 
	{
		cout << "调用成功 !" << endl;
	}
}


//最最最常用的
// 允许派生类向基类的转换 , 派生类可以赋值给基类的指针或引用
// 应用时 + 多态实现不同的调用
#include <thread>
#include <time.h>
void Test03()
{
	//srand(time(0));
	int a = 0;
	cin >> a;
	//while (1)
	//{
		//this_thread::sleep_for(chrono::seconds(1));

		try
		{
			_exception(a);
		}
		//捕获基类的异常就可以达到都捕获
		catch (Exception& excptions)
		{
			//捕获信息
			cout << excptions.what() << endl;
		}
		catch (...)
		{
			cout << "未知异常 " << endl;
		}
	//}
}


///////异常安全问题
//抛出异常可能会导致资源泄露
//这里的解决方式就是智能指针

class Ptr
{

public:

	Ptr()
		:_ptr(new int(1))
	{}

	~Ptr()
	{
		delete _ptr;
		_ptr = nullptr;
		cout << "~Ptr()" << endl;
	}

private:

	int* _ptr;
};



void f(int a)
{
		int* ptr0 = new int(1);
		Ptr* ptr1 = new Ptr();
		Ptr* ptr2 = new Ptr();
		int* ptr3 = new int(2);

		string s("xxxxxxxxx");
		if (a == 2)
		{
			throw s;
		}
		else
		{
			cout << "f()" << endl;
		}
		//这里进行 delete , 但是一旦 throw 抛出异常了 , 这里就执行不到了
		// 就会跳转到 catch 语句中 , 导致资源泄露
		delete ptr0;
		delete ptr1;
		delete ptr2;
		delete ptr3;
}


void Test04()
{
	int a = 0;
	cin >> a;
	
		try
		{
			
			f(a);
		}
	
		catch (const string& str)
		{
			cout << str << endl;
		}

		catch (...)
		{
			cout << "未知异常 " << endl;
		}
}




int main()
{
	//异常与捕获
	//Test01();

	//异常与捕获机制
	//Test02();

	//异常最最最最常用的方式
	//Test03();

	//异常安全问题
	Test04();


	return 0;
}